//***************************************************************************
//*
//*	File:			DialogBuild.cpp
//*	Description:	Dialog for executing builds
//*
//***************************************************************************

//
//--------------------------------------------------------------- PRECOMPILED
//

#include "stdafx.h"

//
//--------------------------------------------------- DECLARATION DEPENDENCIES
//

#include "BuildMgr2.h"

//
//--------------------------------------------------------------- DECLARATION
//

#include "DialogBuild.h"

//
//--------------------------------------------------- DEFINITION DEPENDENCIES
//

#include "DialogProgress.h"

//
//-------------------------------------------------------------- PREPROCESSOR
//

#ifdef	_DEBUG
#define	new DEBUG_NEW
#undef	THIS_FILE
static	char THIS_FILE[] = __FILE__;
#endif

#pragma	warning ( disable : 4239 )	//	passing CString to CArray< CString, CString& >

//
//---------------------------------------------------------------- DEFINITION
//

static CDialogProgress* g_pdlgProgress = NULL;

//
//---------------------------------------------------------------------------------------------------
//*************************************     CON/DESTRUCTION     *************************************
//---------------------------------------------------------------------------------------------------
//

//***************************************************************************************************
//**																					  Constructor	  
//***************************************************************************************************
//**	@DOC		CONSTRUCTION
//**	@MFUNC		Default constructor
//**	@PARM		[in|opt] Pointer to parent window
//**	@END
//***************************************************************************************************
//inline
CDialogBuild::CDialogBuild(CWnd* pParent /*=NULL*/)
	: CDialog(CDialogBuild::IDD, pParent)
{
	//{{AFX_DATA_INIT(CDialogBuild)
	m_iMode						= 1						;
	//}}AFX_DATA_INIT

	this->m_pIApplication		= NULL					;
	this->m_enuFinishAction		= EFinishAction_Continue;
}

//
//---------------------------------------------------------------------------------------------------
//*****************************************     GET/SET     *****************************************
//---------------------------------------------------------------------------------------------------
//

//***************************************************************************************************
//**																				   SetApplication
//***************************************************************************************************
//**	@DOC		SET
//**	@MFUNC		Sets the pointer to the application's interface
//**	@PARM		[in] Pointer to application's interface
//**	@END
//***************************************************************************************************
//inline
void CDialogBuild::SetApplication( IApplication *a_pIApplication )
{
	this->m_pIApplication = a_pIApplication;
}

//
//---------------------------------------------------------------------------------------------------
//***************************************     PERSISTENCE     ***************************************
//---------------------------------------------------------------------------------------------------
//

//***************************************************************************************************
//**																					  LoadProfile 
//***************************************************************************************************
//**	@DOC		PERSISTENCE
//**	@MFUNC		Loads the dialog's settings from the registry
//**	@END
//***************************************************************************************************
//inline
void CDialogBuild::LoadProfile( void )
{
	//
	//	CLEAR LIST CONTROL
	//
	this->m_ctrlListConfigurations.DeleteAllItems();

	//
	//	SETUP VARS
	//
	CString strWorkspacePath	;
	CString strWorkspaceName	;
	CString strProjectPath		;
	CString strProjectName		;
	CString	strConfigurations	;
	CString strSection			;
	UINT	iProjects			= 0;
	UINT	iStyle				= 0;
	DWORD	dwCount				= 0;

	///
	//	GET APP
	//
	CWinApp* pApplication =	::AfxGetApp();

	//
	//	GET SECTION KEY
	//
	CRegKey	keySection;	
			keySection.Attach( pApplication->GetSectionKey( _T( "Configure" ) ) );

	//
	//	READ PROJECT COUNT
	//
	iProjects = pApplication->GetProfileInt( _T( "Configure" ), _T( "NumProjects" ), 0 );

	//
	//	READ PROJECTS
	//
	for ( UINT iProject = 0; iProject < iProjects; iProject++ )
	{
		//
		//	SETUP SECTION
		//
		strSection.Format( "Project.%04d", iProject );

		//
		//	GET SUB KEY
		//
		CRegKey keyProject	;
				keyProject.Open( keySection, strSection );

		//
		//	READ PROJECT
		//
		dwCount = _MAX_PATH; keyProject.QueryValue( strProjectName		.GetBuffer( _MAX_PATH )	, _T( "0.Project.Name"		), &dwCount );	
		dwCount = _MAX_PATH; keyProject.QueryValue( strProjectPath		.GetBuffer( _MAX_PATH )	, _T( "1.Project.Path"		), &dwCount );	
		dwCount = _MAX_PATH; keyProject.QueryValue( strWorkspaceName	.GetBuffer( _MAX_PATH )	, _T( "2.Workspace.Name"	), &dwCount );	
		dwCount = _MAX_PATH; keyProject.QueryValue( strWorkspacePath	.GetBuffer( _MAX_PATH )	, _T( "3.Workspace.Path"	), &dwCount );	
		dwCount = _MAX_PATH; keyProject.QueryValue( strConfigurations	.GetBuffer( _MAX_PATH )	, _T( "4.Configurations"	), &dwCount );	
		dwCount = _MAX_PATH; keyProject.QueryValue( (DWORD&) iStyle								, _T( "5.Style"				) ); 

		strProjectName		.ReleaseBuffer();
		strProjectPath		.ReleaseBuffer();
		strWorkspaceName	.ReleaseBuffer();
		strWorkspacePath	.ReleaseBuffer();
		strConfigurations	.ReleaseBuffer();
		
		//
		//	CLOSE THE KEY
		//
		keyProject.Close();

		//
		//	ADD CONFIGURATIONS
		//
		this->AddProject( strProjectName, strProjectPath, strWorkspaceName, strWorkspacePath, strConfigurations, iStyle );
	}
}

//***************************************************************************************************
//**																					 WriteProfile 
//***************************************************************************************************
//**	@DOC		PERSISTENCE
//**	@MFUNC		Saves the dialog's settings to the registry
//**	@END
//***************************************************************************************************
//inline
void CDialogBuild::WriteProfile( void ) const
{
}

//
//---------------------------------------------------------------------------------------------------
//****************************************     EXECUTION     ****************************************
//---------------------------------------------------------------------------------------------------
//

//***************************************************************************************************
//**																						  Execute
//***************************************************************************************************
//**	@DOC		EXECUTION
//**	@MFUNC		Executes the selected configurations' build process.
//**	@RDESC		<t TRUE> if successful, <t FALSE> otherwise.
//**	@END
//***************************************************************************************************
//inline
BOOL CDialogBuild::Execute( void )
{
	//
	//	CREATE PROGRESS DIALOG
	//
	CDialogProgress dlgProgress( this );
	dlgProgress.Create( IDD_PROGRESS, this );
	dlgProgress.SetApplication( this->m_pIApplication );

	//
	//	This is a bit of a hack:
	//
	//		Store a pointer to the progress dialog for routing
	//		incoming BuildFinished events through to it
	//
	::g_pdlgProgress = &dlgProgress;

	//
	//	SHOW PROGRESS DIALOG
	//
	dlgProgress.ShowWindow( SW_SHOW );

	//
	//	SETUP RESULT
	//
	BOOL bResult = TRUE;

	//
	//	FOR EACH ACTIVE CONFIGURATION
	//
	int iBuilt = 0;

	for ( int iItem = 0; iItem < this->m_ctrlListConfigurations.GetItemCount(); iItem++ )
	{
		//
		//	CHECK WHETHER THIS CONFIGURATION IS ENABLED
		//
		if ( TRUE == this->m_ctrlListConfigurations.GetCheck( iItem ) )
		{
			//
			//	EXECUTE ALL ACTIVE PROJECTS CONTAINING THIS CONFIGURATION
			//
			bResult &= dlgProgress.Execute( this->m_ctrlListConfigurations.GetItemText( iItem, 0 ), static_cast< CDialogProgress::EBuildMode > (this->m_iMode) );

			//
			//	INCREMENT NUMBER OF BUILT CONFIGURATIONS
			//
			++iBuilt;
		}
	}

	//
	//	Stop routing events to the progress dialog
	//
	::g_pdlgProgress = NULL;

	//
	//	HIDE PROGRESS DIALOG
	//
	dlgProgress.ShowWindow( SW_HIDE );

	//
	//	CHECK NUMBER OF BUILT CONFIGURATIONS
	//
	if ( 0 == iBuilt )
	{
		this->MessageBox( "No projects have been built. Skipping shutdown mode evaluation.", "Information", MB_ICONINFORMATION );

		return bResult;
	}

	//
	//	SET FINISH MODE
	//
	this->m_enuFinishAction	= static_cast< EFinishAction > (dlgProgress.m_iFinishedAction);

	//
	//	RETURN RESULT
	//
	return bResult;
}

//
//---------------------------------------------------------------------------------------------------
//*****************************************     HELPERS     *****************************************
//---------------------------------------------------------------------------------------------------
//

//***************************************************************************************************
//**																					   AddProject
//***************************************************************************************************
//**	@DOC		HELPERS
//**	@MFUNC		Adds the specified project to the dialog
//**	@PARM		[in] The project's name
//**	@PARM		[in] The project's path
//**	@PARM		[in] The workspace's name
//**	@PARM		[in] The workspace's path
//**	@PARM		[in] The project's style flags
//**	@END
//***************************************************************************************************
//inline
void CDialogBuild::AddProject( const CString& a_strProjectName, const CString& a_strProjectPath, const CString& a_strWorkspaceName, const CString& a_strWorkspacePath, const CString& a_strConfigurations, int a_iStyle )
{
	//
	//	SPLIT CONFIGURATIONS
	//
	CArray< CString, CString& >	arrConfigurations;
	CString						strConfigurations( a_strConfigurations );
	CString						strConfiguration ;

	int iPos = strConfigurations.Find( ';' );

	while ( iPos >= 0 )
	{
		//
		//	ADD CONFIGURATION
		//
		arrConfigurations.Add( strConfigurations.Left( iPos ) );

		//
		//	REMOVE CONFIGURATION
		//
		strConfigurations = strConfigurations.Right( strConfigurations.GetLength() - iPos - 1 );

		//
		//	FIND NEXT CONFIGURATION
		//
		iPos = strConfigurations.Find( ';' );
	}

	if ( ! strConfigurations.IsEmpty() )
	{
		arrConfigurations.Add( strConfigurations );
	}

	//
	//	ADD MISSING CONFIGURATIONS TO THE LIST CONTROL [i know, this can be optimized]
	//
	for ( int iConfiguration = 0; iConfiguration < arrConfigurations.GetSize(); iConfiguration++ )
	{
		//
		//	SETUP SKIP INDICATOR
		//
		bool bSkip = false;

		//
		//	CHECK EACH ITEM
		//
		for ( int iItem = 0; iItem < this->m_ctrlListConfigurations.GetItemCount(); iItem++ )
		{
			strConfiguration = this->m_ctrlListConfigurations.GetItemText( iItem, 0 );

			if ( strConfiguration == arrConfigurations[ iConfiguration ] )
			{
				bSkip = true;	// skip this configuration
				break;			// stop searching
			}
		}

		//
		//	CHECK FOR SKIP
		//
		if ( true == bSkip )
		{
			continue;
		}

		//
		//	OK, ADD THE CONFIGURATION
		//
		this->m_ctrlListConfigurations.InsertItem( 0, arrConfigurations[ iConfiguration ], 0 );
	}	
}

//***************************************************************************************************
//**																						 MoveItem
//***************************************************************************************************
//**	@DOC		HELPERS
//**	@PARM		Swaps the contents of the items at the specified positions
//**	@PARM		[in] Source item's index
//**	@PARM		[in] Destination item's index
//**	@END
//***************************************************************************************************
//inline
void CDialogBuild::MoveItem( int a_iSrc, int a_iDst )
{
	//
	//	CHECK VALIDITY
	//
	if ( a_iSrc <  0											 )	return;
	if ( a_iDst <  0											 )	return;
	if ( a_iSrc >= this->m_ctrlListConfigurations.GetItemCount() )	return;
	if ( a_iDst >= this->m_ctrlListConfigurations.GetItemCount() )	return;

	//
	//	SETUP VARS
	//
	CString	strSwap	 ;
	CString	strTemp	 ;
	int		iSubItem = 0;

	//
	//	SWAP TEXTs
	//
	for ( iSubItem = 0; iSubItem < 1; iSubItem++ )
	{
		//
		//	STORE DESTINATION IN SWAP MEM
		//
		strSwap = this->m_ctrlListConfigurations.GetItemText( a_iDst, iSubItem );

		//
		//	COPY SOURCE TO DESTINATION
		//
		strTemp = this->m_ctrlListConfigurations.GetItemText( a_iSrc, iSubItem );
				  this->m_ctrlListConfigurations.SetItemText( a_iDst, iSubItem, strTemp.GetBuffer( _MAX_PATH ) );

		strTemp.ReleaseBuffer();

		//
		//	COPY SWAP TO SOURCE
		//
		this->m_ctrlListConfigurations.SetItemText( a_iSrc, iSubItem, strSwap.GetBuffer( _MAX_PATH ) );

		strSwap.ReleaseBuffer();
	}

	//
	//	SWAP ITEM STATES
	//
	UINT uiSrc = this->m_ctrlListConfigurations.GetItemState( a_iSrc, (UINT) -1 );
	UINT uiDst = this->m_ctrlListConfigurations.GetItemState( a_iDst, (UINT) -1 );

	this->m_ctrlListConfigurations.SetItemState( a_iSrc, uiDst, (UINT) -1 );
	this->m_ctrlListConfigurations.SetItemState( a_iDst, uiSrc, (UINT) -1 );
}

//
//---------------------------------------------------------------------------------------------------
//****************************************     VIRTUALS     *****************************************
//---------------------------------------------------------------------------------------------------
//

//***************************************************************************************************
//**																				   DoDataExchange
//***************************************************************************************************
//**	@DOC		VIRTUALS
//**	@MFUNC		Exchanges data between the dialog's members and its controls
//**	@PARM		[in/out] Pointer to data exchange context
//**	@END
//***************************************************************************************************
//inline
void CDialogBuild::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange( pDX );

	//{{AFX_DATA_MAP(CDialogBuild)
	DDX_Control( pDX, IDC_CONFIGURATIONS, m_ctrlListConfigurations	);
	DDX_CBIndex( pDX, IDC_BUILD			, m_iMode					);
	//}}AFX_DATA_MAP
}

//
//---------------------------------------------------------------------------------------------------
//****************************************     MESSAGES     *****************************************
//---------------------------------------------------------------------------------------------------
//

BEGIN_MESSAGE_MAP( CDialogBuild, CDialog )
	//{{AFX_MSG_MAP(CDialogBuild)
	ON_BN_CLICKED	(				IDC_MOVE_UP			, OnMoveUp				)
	ON_BN_CLICKED	(				IDC_MOVE_DOWN		, OnMoveDown			)
	ON_NOTIFY		( NM_CLICK	,	IDC_CONFIGURATIONS	, OnClickConfigurations	)
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

//***************************************************************************************************
//**																					 OnInitDialog
//***************************************************************************************************
//**	@DOC		MESSAGES
//**	@MFUNC		Called to initialize the dialog
//**	@END
//***************************************************************************************************
//inline
BOOL CDialogBuild::OnInitDialog() 
{
	//
	//	CALL BASE CLASS' IMPLEMENTATION
	//
	CDialog::OnInitDialog();

	//
	//	MODIFY LIST CONTROL
	//
	this->m_ctrlListConfigurations.InsertColumn( 0, _T( "Configuration" ), LVCFMT_LEFT, 250, 0 );
	this->m_ctrlListConfigurations.SetExtendedStyle( LVS_EX_FULLROWSELECT | LVS_EX_CHECKBOXES );

	//
	//	READ PROFILE
	//
	this->LoadProfile();

	//
	//	ENABLE AUTO-FOCUS
	//
	return TRUE;  
}

//***************************************************************************************************
//**																							 OnOK
//***************************************************************************************************
//**	@DOC		MESSAGES
//**	@MFUNC		Called when OK button is clicked
//**	@COMM		Executes the current settings, writes the profile and closes the dialog
//**	@END
//***************************************************************************************************
//inline
void CDialogBuild::OnOK() 
{
	//
	//	EXECUTE
	//
	this->Execute();												

	//
	//	WRITE PROFILE
	//
	this->WriteProfile();

	//
	//	CALL BASE CLASS' IMPLEMENTATION
	//
	CDialog::OnOK();
}

//***************************************************************************************************
//**																						 OnMoveUp
//***************************************************************************************************
//**	@DOC		MESSAGES
//**	@MFUNC		Called when the MoveUp button is pressed
//**	@COMM		Moves all selected items one place up while taking care of boundaries.
//**	@END
//***************************************************************************************************
//inline
void CDialogBuild::OnMoveUp() 
{
	//
	//	SETUP VARS
	//
	int	iPredecessor = 0;
	int	iItem		 = 0;

	//
	//	ENUMERATE SELECTED ITEMS
	//
	POSITION pos = this->m_ctrlListConfigurations.GetFirstSelectedItemPosition();

	while ( NULL != pos )
	{
		//
		//	GET NEXT SELECTED ITEM'S INDEX
		//
		iItem = this->m_ctrlListConfigurations.GetNextSelectedItem( pos );

		//
		//	CHECK WHETHER WE CAN MOVE THAT ITEM
		//
		if ( iItem > iPredecessor )
		{
			//
			//	MOVE THAT ITEM
			//
			this->MoveItem( iItem, iItem - 1 );

			//
			//	KEEP ANY FOLLOWING ITEM FROM REPLACING THIS ITEM
			//
			iPredecessor = iItem;
		}
		else
		{
			//
			//	THIS ITEM COULD NOT BE MOVED
			//
			++iPredecessor;
		}
	}

	//
	//	ENSURE THAT OUR SELECTION IS VISIBLE
	//
	pos = this->m_ctrlListConfigurations.GetFirstSelectedItemPosition();

	if ( NULL != pos )
	{
		//
		//	GET FIRST SELECTED ITEM'S POSITION
		//
		iItem = this->m_ctrlListConfigurations.GetNextSelectedItem( pos );

		//
		//	ENSURE THAT ITEMS's VISIBILITY
		//
		this->m_ctrlListConfigurations.EnsureVisible( iItem, FALSE );
	}

	//
	//	SET THE FOCUS BACK TO THE LIST CONTROL (neccessary in order to have the control re-display its selection)
	//
	this->m_ctrlListConfigurations.SetFocus();
}

//***************************************************************************************************
//**																					   OnMoveDown
//***************************************************************************************************
//**	@DOC		MESSAGES
//**	@MFUNC		Called when the MoveDown button is pressed
//**	@COMM		Moves all selected items one place down while taking care of boundaries.
//**	@END
//***************************************************************************************************
//inline
void CDialogBuild::OnMoveDown() 
{
	//
	//	GATHER SELECTED INDICES (for we must process them in reverse order)
	//
	CArray< int, int >	arrSelected	;
	POSITION			pos			= this->m_ctrlListConfigurations.GetFirstSelectedItemPosition();

	if ( NULL == pos )
	{
		return;	// avoid messing around with GetUpperBound()
	}

	while ( NULL != pos )
	{
		//
		//	GET NEXT SELECTED ITEM'S INDEX
		//
		arrSelected.Add( this->m_ctrlListConfigurations.GetNextSelectedItem( pos ) );
	}

	//
	//	NOW, PROCESS THE ITEMS
	//
	int iSuccessor  = this->m_ctrlListConfigurations.GetItemCount() - 1;
	int iItem		= iSuccessor;
	int iSelected	= 0;

	for ( iSelected = arrSelected.GetUpperBound(); iSelected >= 0; iSelected-- )
	{
		//
		//	GET ITEM INDEX
		//
		iItem = arrSelected[ iSelected ];

		//
		//	CHECK WHETHER WE CAN MOVE THE ITEM
		//
		if ( iItem < iSuccessor )
		{
			//
			//	MOVE THAT ITEM
			//
			this->MoveItem( iItem, iItem + 1 );

			//
			//	PREVENT THAT ITEM FROM BEING OVERRIDEEN
			//
			iSuccessor = iItem;
		}
		else
		{
			//
			//	COULD NOT MOVE THAT ITEM
			//
			--iSuccessor;
		}
	}

	//
	//	ENSURE THAT OUR SELECTION IS VISIBLE
	//
	pos	= this->m_ctrlListConfigurations.GetFirstSelectedItemPosition();

	while ( NULL != pos )
	{
		//
		//	GET LAST SELECTED ITEM'S POSITION
		//
		iItem = this->m_ctrlListConfigurations.GetNextSelectedItem( pos );
	}

	//
	//	ENSURE THAT ITEMS's VISIBILITY
	//
	this->m_ctrlListConfigurations.EnsureVisible( iItem, FALSE );

	//
	//	SET THE FOCUS BACK TO THE LIST CONTROL (neccessary in order to have the control re-display its selection)
	//
	this->m_ctrlListConfigurations.SetFocus();
}

//***************************************************************************************************
//**																			OnClickConfigurations				  
//***************************************************************************************************
//**	@DOC		MESSAGES
//**	@MFUNC		Called when the user clicks within the configurations' list control
//**	@COMM		Assures that when the user (un-)checks a selected item, all other selected items
//**				will have the same check state.
//**	@END
//***************************************************************************************************
//inline
void CDialogBuild::OnClickConfigurations( NMHDR* pNMHDR, LRESULT* pResult ) 
{
	//
	//	GET LIST VIEW NOTIFICATION HEADER
	//
	NMLISTVIEW* pNMListView = reinterpret_cast< NMLISTVIEW* > (pNMHDR);

	//
	//	WE'RE ONLY INTERESTED IN CLICKS ON A SELECTED ITEM'S CHECK BOX
	//
	if ( pNMListView->ptAction.x >= 16 )	
	{
		return;								// not the check box [@HACK: assumes checkboxes are always 16 pixels wide]
	}

	if ( LVIS_SELECTED != this->m_ctrlListConfigurations.GetItemState( pNMListView->iItem, LVIS_SELECTED ) )
	{
		return;								// not selected
	}
	
	//
	//	GET THE ITEM's CHECK STATE
	//
	BOOL bChecked = this->m_ctrlListConfigurations.GetCheck( pNMListView->iItem );

	//
	//	SET INVERSE CHECK STATE FOR ALL SELECTED ITEMS EXCEPT THIS ONE
	//
	POSITION pos = this->m_ctrlListConfigurations.GetFirstSelectedItemPosition();

	while( NULL != pos )
	{
		//
		//	GET NEXT SELECTED ITEM'S INDEX
		//
		int iItem = this->m_ctrlListConfigurations.GetNextSelectedItem( pos );

		//
		//	CHECK FOR CURRENT ITEM
		//
		if ( iItem == pNMListView->iItem )
		{
			continue;
		}

		//
		//	SET INVERSE CHECK
		//
		this->m_ctrlListConfigurations.SetCheck( iItem, bChecked ? FALSE : TRUE );
	}

	//
	//	RETURN RESULT
	//
	*pResult = 0;
}

//***************************************************************************************************
//**																				  OnBuildFinished
//***************************************************************************************************
//**	@DOC		MESSAGES
//**	@MFUNC		Called when DevStuido signals that a build has finished
//**	@PARM		[in] The number of errors
//**	@PARM		[in] The number of warnings
//**	@COMM		This is a plain proxy method that delegates the event further to the current 
//**				progress dialog (if any).
//**	@END
//***************************************************************************************************
//inline
void CDialogBuild::OnBuildFinished( long a_lErrors, long a_lWarnings )
{
	//
	//	CHECK WHETHER WE ARE URRENTLY IN THE MIDDLE OF A BUILD
	//
	if ( NULL == ::g_pdlgProgress )	
	{
		return;
	}

	//
	//	ROUTE THE EVENT TO THE PROGRESS DIALOG
	//
	::g_pdlgProgress->OnBuildFinished( a_lErrors, a_lWarnings );
}
